package com.github.hiling.common.filter;

import com.alibaba.fastjson2.JSON;
import com.github.hiling.common.config.BaseConfig;
import com.github.hiling.common.utils.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;

import java.util.ArrayList;
import java.util.List;

@Aspect
@Component
@Slf4j
public class ApiLogAspect extends BaseConfig {

    /**
     * 控制器切点
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)"
            + " || @annotation(org.springframework.web.bind.annotation.RestController)"
            + " || @annotation(org.springframework.web.bind.annotation.GetMapping)"
            + " || @annotation(org.springframework.web.bind.annotation.PostMapping)"
            + " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)"
            + " || @annotation(org.springframework.web.bind.annotation.PutMapping)"
            + " || @annotation(org.springframework.web.bind.annotation.PatchMapping)"
    )
    public void controllerAspect() {

    }

    /**
     * 返回通知
     */
    @Before(value = "controllerAspect()")
    public void beforeReturnMethod(JoinPoint joinPoint) {
        if (loggingHttpRequest) {
            beforeLog(joinPoint);
        }
    }

    /**
     * 返回通知
     *
     * @param resp      此参数值必须和方法签名里面的resp一致
     */
    @AfterReturning(pointcut = "controllerAspect()", returning = "resp")
    public void afterReturnMethod(JoinPoint joinPoint, Object resp) {
        if (loggingHttpResponse) {
            afterLog(joinPoint, resp);
        }
    }

    /**
     * 抛出通知
     *
     * @param ex        此参数值必须和方法签名里面的ex一致
     */
    @AfterThrowing(pointcut = "controllerAspect()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) {
        afterLog(joinPoint, ex);
    }


    /**
     * 处理请求日志
     */
    public void beforeLog(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();

        //只有打印response时，tracing才有用
        log.info(">>>>>>请求日志：{}", RequestUtils.getRequestInfo(request, loggingHttpRequestBody));
    }

    /**
     * 处理响应日志
     */
    public void afterLog(JoinPoint joinPoint, Object object) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();

        Object[] args = joinPoint.getArgs();
        List<Object> printArgs = new ArrayList<>();
        for (Object obj : args) {
            //如果obj不是HttpServletRequest类型时进行JSON序列化，在异步请求中，对Request序列化会抛异常
            if (!(obj instanceof HttpServletRequest)) {
                printArgs.add(obj);
            }
        }

        log.debug(">>>>>>响应日志：[{}] resp={} args={}",
                RequestUtils.getRequestInfo(request,loggingHttpRequestBody),
                JSON.toJSONString(object),
                JSON.toJSONString(printArgs)
        );
    }
}
